﻿#include "client_gui.hpp"

#include <iostream>
#include <QScrollBar>
#include <QFileDialog>
#include <QTimer>

#include "device_info.hpp"
#include "network_interface.hpp"
#include "system_time_ctl.hpp"
#include "user.hpp"
#include "ui_client_gui.h"

constexpr uint16_t ListenPortMulticast = 5555;
constexpr uint16_t ListenPortBroadcast = 6666;
const std::string MulticastAddress = "224.1.1.100";

ClientGUI::ClientGUI(QWidget* parent) :
    QWidget(parent), ui(new Ui::ClientGUI)
    , client_id{User::generate_serial("", 20)}
{
    ui->setupUi(this);

    initUI();

    QTimer* timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &ClientGUI::onLocalTimerTimeout);
    timer->start(100);
    onLocalTimerTimeout();
}

ClientGUI::~ClientGUI()
{
    delete ui;
    if (sp_broadcast_acceptor != nullptr)
    {
        sp_broadcast_acceptor->release();
        if (thread_acceptor.joinable())
        {
            thread_acceptor.join();
        }
        sp_broadcast_acceptor.reset();
    }
    if (sp_broadcast_sender != nullptr)
    {
        sp_broadcast_sender->release();
        sp_broadcast_sender.reset();
    }
    if (sp_multicast_acceptor != nullptr)
    {
        sp_multicast_acceptor->release();
        if (thread_acceptor.joinable())
        {
            thread_acceptor.join();
        }
        sp_multicast_acceptor.reset();
    }
    if (sp_multicast_sender != nullptr)
    {
        sp_multicast_sender->release();
        sp_multicast_sender.reset();
    }
}

void ClientGUI::initUI()
{
    setWindowTitle(u8"设备查找工具");
    setWindowIcon(QIcon(":/favicon.ico"));

    // ui->client_cfg->setTitle(QString::fromStdString(u8"当前通信 ID: " + client_id));

    auto interfaces = NetworkInterface::getActiveInterfaces();
    for (const auto& item : interfaces)
    {
        ui->interface_choice->addItem(QString::fromStdString(item.name + "::" + item.description),
                                      item.name.c_str());
    }

    connect(ui->btn_create_connection, &QPushButton::clicked, this, &ClientGUI::onClickedCreateConnection);
    ui->btn_create_connection->setText(u8"创建连接");

    connect(ui->btn_refresh_device_list, &QPushButton::clicked, this, &ClientGUI::onClickedRefreshDeviceList);

    connect(this, &ClientGUI::signalLog, this, &ClientGUI::handleLogSignal);


    ui->ctl_area->setEnabled(false);
    ui->client_cfg->setEnabled(true);

    connect(ui->btn_log_area_clear, &QPushButton::clicked, this, &ClientGUI::onClickedLogAreaClear);
    connect(ui->btn_log_content_export, &QPushButton::clicked, this, &ClientGUI::onClickedLogContentExport);
    ui->log_browser->clear();
    ui->btn_log_content_export->setEnabled(false);
    ui->btn_log_area_clear->setEnabled(false);

    connect(ui->btn_sync_time, &QPushButton::clicked, this, &ClientGUI::onClickedSyncDeviceTime);
    connect(ui->btn_submit_info, &QPushButton::clicked, this, &ClientGUI::onClickedSubmitDeviceInfo);


    model_device_list = new QStringListModel(this);
    ui->device_list->setModel(model_device_list);
    connect(ui->device_list, &QListView::clicked, this, &ClientGUI::onClickedDeviceList);

    ui->device_serial->setReadOnly(true);
    ui->local_time->setReadOnly(true);


    connect(this, &ClientGUI::signalDeviceSearch, this, &ClientGUI::handelSignalDeviceSearch);

    ui->use_multicast->setChecked(true);
}

void ClientGUI::onClickedCreateConnection()
{
    static bool flag_connected = false;
    if (flag_connected == true)
    {
        if (ui->use_broadcast->isChecked())
        {
            sp_broadcast_acceptor->release();
            if (thread_acceptor.joinable())
            {
                thread_acceptor.join();
            }
            sp_broadcast_acceptor.reset();
            sp_broadcast_sender->release();
            sp_broadcast_sender.reset();
            emit signalLog(QString::fromStdString(u8"广播连接已关闭"));
        }
        else
        {
            sp_multicast_acceptor->release();
            if (thread_acceptor.joinable())
            {
                thread_acceptor.join();
            }
            sp_multicast_acceptor.reset();
            sp_multicast_sender->release();
            sp_multicast_sender.reset();
            emit signalLog(QString::fromStdString(u8"组播连接已关闭"));
        }
        ui->ctl_area->setEnabled(false);
        ui->client_cfg->setEnabled(true);
        ui->btn_create_connection->setText(u8"创建连接");
        ui->device_name->setText("");
        ui->device_serial->setText("");
        ui->device_remark->setText("");
        ui->device_ip->setText("");
        ui->device_subnet->setText("");
        ui->device_ntp_url->setText("");
        model_device_list->setStringList(QStringList{});
        current_device_serial.clear();
        flag_connected = false;
    }
    else
    {
        if (ui->use_broadcast->isChecked())
        {
            sp_broadcast_acceptor = std::make_shared<BroadcastAcceptor>(
                [this](const std::vector<uint8_t>& data, const std::string& ip, uint16_t port)
                {
                    onDataReceived(data, ip, port);
                });
            sp_broadcast_sender = std::make_shared<BroadcastSender>();
            auto interfaces = NetworkInterface::getActiveInterfaces();
            QString interface_name = ui->interface_choice->currentData().toString();
            std::string InterfaceAddress = "0.0.0.0";
            for (const auto& item : interfaces)
            {
                if (item.name == interface_name.toStdString())
                {
                    InterfaceAddress = item.Addresses[0].first;
                }
            }
            auto ret = sp_broadcast_acceptor->init(ListenPortBroadcast, "0.0.0.0");
            if (ret.isOK() == false)
            {
                emit signalLog(QString::fromStdString(u8"广播数据接收器初始化失败."));
                sp_broadcast_acceptor->release();
                sp_broadcast_acceptor.reset();
                return;
            }
            thread_acceptor = std::thread([this]()
            {
                sp_broadcast_acceptor->run();
            });
            emit signalLog(QString(u8"广播数据接收器初始化成功。组播地址：%1::%2, 本地接口：%3。").arg(QString::fromStdString("255.255.255.255"),
                                                                               QString::number(ListenPortBroadcast),
                                                                               QString::fromStdString(
                                                                                   InterfaceAddress)));

            ret = sp_broadcast_sender->init(ListenPortBroadcast, InterfaceAddress);
            if (ret.isOK() == false)
            {
                emit signalLog(QString::fromStdString(u8"广播数据发送器初始化失败."));
                sp_broadcast_sender->release();
                sp_broadcast_sender.reset();
                return;
            }
            emit signalLog(QString(u8"广播数据发送器初始化成功。目标地址：%1::%2, 本地接口：%3。").arg(QString::fromStdString("255.255.255.255"),
                                                                               QString::number(ListenPortBroadcast),
                                                                               QString::fromStdString(
                                                                                   InterfaceAddress)));
            emit signalLog(QString::fromStdString(u8"广播连接已建立"));
        }
        else
        {
            sp_multicast_acceptor = std::make_shared<MulticastAcceptor>(
                [this](const std::vector<uint8_t>& data, const std::string& ip, uint16_t port)
                {
                    onDataReceived(data, ip, port);
                });
            sp_multicast_sender = std::make_shared<MulticastSender>();
            auto interfaces = NetworkInterface::getActiveInterfaces();
            QString interface_name = ui->interface_choice->currentData().toString();
            std::string InterfaceAddress = "0.0.0.0";
            for (const auto& item : interfaces)
            {
                if (item.name == interface_name.toStdString())
                {
                    InterfaceAddress = item.Addresses[0].first;
                }
            }
            const std::string address_multicast = "224.1.1.100";
            auto ret = sp_multicast_acceptor->init(ListenPortMulticast, address_multicast, InterfaceAddress);
            if (ret.isOK() == false)
            {
                emit signalLog(QString::fromStdString(u8"组播数据接收器初始化失败."));
                sp_multicast_acceptor->release();
                sp_multicast_acceptor.reset();
                return;
            }
            thread_acceptor = std::thread([this]()
            {
                sp_multicast_acceptor->run();
            });
            emit signalLog(QString(u8"组播数据接收器初始化成功。组播地址：%1::%2, 本地接口：%3。").arg(QString::fromStdString(MulticastAddress),
                                                                               QString::number(ListenPortMulticast),
                                                                               QString::fromStdString(
                                                                                   InterfaceAddress)));

            ret = sp_multicast_sender->init(address_multicast, ListenPortMulticast, InterfaceAddress);
            if (ret.isOK() == false)
            {
                emit signalLog(QString::fromStdString(u8"组播数据发送器初始化失败."));
                sp_multicast_sender->release();
                sp_multicast_sender.reset();
                return;
            }
            emit signalLog(QString(u8"组播数据发送器初始化成功。目标地址：%1::%2, 本地接口：%3。").arg(QString::fromStdString(MulticastAddress),
                                                                               QString::number(ListenPortMulticast),
                                                                               QString::fromStdString(
                                                                                   InterfaceAddress)));
            emit signalLog(QString::fromStdString(u8"组播连接已建立"));
        }
        ui->client_cfg->setEnabled(false);
        ui->ctl_area->setEnabled(true);
        ui->btn_create_connection->setText(u8"关闭连接");
        flag_connected = true;
    }
}

void ClientGUI::onDataReceived(const std::vector<uint8_t>& data, const std::string& ip, uint16_t port)
{
    std::string str{data.begin(), data.end()};
    // emit signalLog(QString(u8"onDataReceived:: %1").arg(QString::fromStdString(str)));
    nlohmann::json rsp_json;
    try
    {
        rsp_json = nlohmann::json::parse(str);
    }
    catch (const std::exception& e)
    {
        emit signalLog(QString(u8"数据解析失败。%1").arg(QString::fromStdString(e.what())));
        return;
    }

    if (rsp_json["type"].is_null())
    {
        // emit signalLog(QString::fromStdString(u8"缺少必要字段。"));
        return;
    }

    if (rsp_json["type"] != "response")
    {
        // emit signalLog(QString::fromStdString(u8"协议类型错误。"));
        return;
    }
    if (rsp_json["client_id"].is_null() || rsp_json["client_id"] != client_id)
    {
        // emit signalLog(QString::fromStdString(u8"客户端 ID 不匹配。"));
        return;
    }
    if (rsp_json["url"].is_null() || rsp_json["data"].is_null())
    {
        emit signalLog(QString::fromStdString(u8"缺少必要字段。"));
        return;
    }

    if (rsp_json["url"] == "/device/search")
    {
        onHandleDeviceSearch(rsp_json["data"], ip, port);
    }
    else if (rsp_json["url"] == "/device/modify")
    {
        onHandleDeviceModify(rsp_json["data"], ip, port);
    }
    else if (rsp_json["url"] == "/device/time/set")
    {
        onHandleDeviceTimeSet(rsp_json["data"], ip, port);
    }
    else if (rsp_json["url"] == "/device/time/get")
    {
        onHandleDeviceTimeGet(rsp_json["data"], ip, port);
    }
    else
    {
        emit signalLog(QString::fromStdString(u8"未知的 URL。"));
    }
}

void ClientGUI::onClickedRefreshDeviceList()
{
    ui->device_name->setText("");
    ui->device_serial->setText("");
    ui->device_remark->setText("");
    ui->device_ip->setText("");
    ui->device_subnet->setText("");
    ui->device_ntp_url->setText("");
    model_device_list->setStringList(QStringList{});
    current_device_serial.clear();

    nlohmann::json data;
    data["client_id"] = client_id;
    if (sendRequest("/device/search", data))
    {
        emit signalLog(QString(u8"发送设备查找请求成功。"));
    }
    else
    {
        emit signalLog(QString::fromStdString(u8"发送设备查找请求失败。"));
    }
}

bool ClientGUI::sendRequest(const std::string& url, const nlohmann::json& data) const
{
    if (ui->use_broadcast->isChecked())
    {
        if (sp_broadcast_sender == nullptr)
        {
            return false;
        }
    }
    else
    {
        if (sp_multicast_sender == nullptr)
        {
            return false;
        }
    }

    nlohmann::json json;
    json["type"] = "request";
    json["url"] = url;
    json["data"] = data;
    std::string json_str = json.dump();
    std::vector<uint8_t> buffer;
    buffer.resize(json_str.size());
    std::copy(json_str.begin(), json_str.end(), buffer.begin());
    if (ui->use_broadcast->isChecked())
    {
        auto ret = sp_broadcast_sender->send(buffer);
        return ret;
    }
    else
    {
        auto ret = sp_multicast_sender->send(buffer);
        return ret;
    }
}

void ClientGUI::handleLogSignal(const QString& message) const
{
    QString log = QString{"[%1] - %2"}.arg(QDateTime::currentDateTime().toString("yyyy-dd-MM hh:mm:ss.zzz"), message);
    ui->log_browser->append(log);
    ui->log_browser->verticalScrollBar()->setValue(ui->log_browser->verticalScrollBar()->maximum());
    ui->btn_log_area_clear->setEnabled(true);
    ui->btn_log_content_export->setEnabled(true);
}

void ClientGUI::onClickedLogAreaClear() const
{
    ui->log_browser->clear();
    ui->btn_log_content_export->setEnabled(false);
    ui->btn_log_area_clear->setEnabled(false);
}

void ClientGUI::onClickedLogContentExport()
{
    auto log_content = ui->log_browser->toPlainText();
    if (log_content.isEmpty())
    {
        emit signalLog(QString::fromStdString(u8"日志内容为空，无需导出"));
        return;
    }
    QString file_path = QFileDialog::getSaveFileName(this, u8"导出日志",
                                                     QString::fromStdString(u8"log-%1.txt").arg(
                                                         QDateTime::currentDateTime().toString(
                                                             "yyyy_dd_MM_hh_mm_ss_zzz")),
                                                     u8"文本文件 (*.txt);;所有文件 (*)");
    if (file_path.isEmpty())
    {
        emit signalLog(QString::fromStdString(u8"导出日志取消"));
    }
    else
    {
        QFile file(file_path);
        if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
        {
            emit signalLog(QString::fromStdString(u8"导出日志失败"));
        }
        else
        {
            file.write(log_content.toUtf8());
            file.close();
        }
    }
}

void ClientGUI::onClickedSyncDeviceTime()
{
    if (current_device_serial.empty())
    {
        emit signalLog(QString::fromStdString(u8"当前未选择任何设备"));
        return;
    }
    DeviceInfo device_info;
    try
    {
        device_info = map_device_info.at(current_device_serial);
    }
    catch (std::out_of_range& e)
    {
        emit signalLog(
            QString::fromStdString(u8"当前设备信息不存在。serial = %1。%2").arg(QString::fromStdString(current_device_serial),
                                                                     QString::fromStdString(e.what())));
        return;
    }

    nlohmann::json data;
    data["client_id"] = client_id;
    data["serial"] = device_info.serial;
    data["timestamp_ms"] = SystemTimeCtl::getCurrentTime();
    if (sendRequest("/device/time/set", data))
    {
        emit signalLog(QString::fromStdString(u8"发送设备时间同步请求成功。"));
    }
    else
    {
        emit signalLog(QString::fromStdString(u8"发送设备时间同步请求失败。"));
    }
}

static std::vector<std::string> SplitString(const std::string& str, const std::string& pattern)
{
    std::vector<std::string> result;
    if (str.empty())
        return result;

    size_t start = 0;
    size_t end = str.find(pattern);

    while (end != std::string::npos)
    {
        result.push_back(str.substr(start, end - start));
        start = end + pattern.length();
        end = str.find(pattern, start);
    }

    result.push_back(str.substr(start));
    return result;
}

static bool ValidAddressFormat(const std::string& address)
{
    std::vector<std::string> parts = SplitString(address, ".");
    if (parts.size() != 4)
    {
        return false;
    }
    for (const auto& part : parts)
    {
        try
        {
            int num = std::stoi(part);
            if (num < 0 || num > 255)
            {
                return false;
            }
        }
        catch (std::exception&)
        {
            return false;
        }
    }
    return true;
}

void ClientGUI::onClickedSubmitDeviceInfo()
{
    if (current_device_serial.empty())
    {
        emit signalLog(QString::fromStdString(u8"当前未选择任何设备"));
        return;
    }

    std::string name = ui->device_name->text().toStdString();
    std::string serial = ui->device_serial->text().toStdString();
    std::string remark = ui->device_remark->text().toStdString();
    std::string ip = ui->device_ip->text().toStdString();
    std::string subnet = ui->device_subnet->text().toStdString();
    std::string ntp_url = ui->device_ntp_url->text().toStdString();
    DeviceInfo device_info;
    try
    {
        device_info = map_device_info.at(current_device_serial);
    }
    catch (std::out_of_range& e)
    {
        emit signalLog(
            QString::fromStdString(u8"当前设备信息不存在。serial = %1。%2").arg(QString::fromStdString(current_device_serial),
                                                                     QString::fromStdString(e.what())));
        return;
    }


    if (name.empty() || serial.empty() || ip.empty() || subnet.empty() || ntp_url.empty())
    {
        emit signalLog(QString::fromStdString(u8"错误：名称、序列号、IP地址、子网掩码和NTP URL不能为空！"));
        return;
    }

    if (ValidAddressFormat(ip) == false)
    {
        emit signalLog(QString::fromStdString(u8"错误：IP地址格式错误！"));
        return;
    }
    if (ValidAddressFormat(subnet) == false)
    {
        emit signalLog(QString::fromStdString(u8"错误：子网掩码格式错误！"));
        return;
    }

    if (serial != device_info.serial
        || name != device_info.name
        || remark != device_info.remark
        || ip != device_info.ip
        || subnet != device_info.subnet
        || ntp_url != device_info.ntp_url)
    {
        nlohmann::json data;
        data["client_id"] = client_id;
        data["serial"] = serial;
        data["name"] = name;
        data["remark"] = remark;
        data["ip"] = ip;
        data["subnet"] = subnet;
        data["ntp_url"] = ntp_url;
        if (sendRequest("/device/modify", data))
        {
            emit signalLog(QString::fromStdString(u8"发送修改设备信息请求成功。"));
            onClickedRefreshDeviceList();
        }
        else
        {
            emit signalLog(QString::fromStdString(u8"发送修改设备信息请求失败。"));
        }
    }
    else
    {
        emit signalLog(QString::fromStdString(u8"设备信息未修改。"));
    }
}

void ClientGUI::onHandleDeviceSearch(const nlohmann::json& body, const std::string& ip, uint16_t port)
{
    emit signalLog(QString(u8"收到设备查找响应。来自：%1::%2。内容：%3")
        .arg(QString::fromStdString(ip),
             QString::number(port), QString::fromStdString(body.dump())));

    emit signalDeviceSearch(QString::fromStdString(body.dump()));
}

void ClientGUI::onHandleDeviceModify(const nlohmann::json& body, const std::string& ip, uint16_t port)
{
    emit signalLog(QString(u8"收到设备修改响应。来自：%1::%2。内容：%3")
        .arg(QString::fromStdString(ip), QString::number(port), QString::fromStdString(body.dump())));
}

void ClientGUI::onHandleDeviceTimeSet(const nlohmann::json& body, const std::string& ip, uint16_t port)
{
    emit signalLog(QString(u8"收到设备时间修改响应。来自：%1::%2。内容：%3")
        .arg(QString::fromStdString(ip),
             QString::number(port),
             QString::fromStdString(body.dump())));
}

void ClientGUI::onHandleDeviceTimeGet(const nlohmann::json& body,
                                      const std::string& ip, uint16_t port)
{
}

void ClientGUI::onClickedDeviceList(const QModelIndex& index)
{
    if (index.row() == -1)
    {
        return;
    }

    current_device_serial = model_device_list->stringList().at(index.row()).toStdString();
    if (map_device_info.find(current_device_serial) == map_device_info.end())
    {
        emit signalLog(QString(u8"当前设备信息不存在。serial = %1。").arg(QString::fromStdString(current_device_serial)));
        return;
    }

    ui->device_name->setText(QString::fromStdString(map_device_info[current_device_serial].name));
    ui->device_serial->setText(QString::fromStdString(map_device_info[current_device_serial].serial));
    ui->device_remark->setText(QString::fromStdString(map_device_info[current_device_serial].remark));
    ui->device_ip->setText(QString::fromStdString(map_device_info[current_device_serial].ip));
    ui->device_subnet->setText(QString::fromStdString(map_device_info[current_device_serial].subnet));
    ui->device_ntp_url->setText(QString::fromStdString(map_device_info[current_device_serial].ntp_url));
}

void ClientGUI::onLocalTimerTimeout() const
{
    ui->local_time->setText(QDateTime::currentDateTime().toString("yyyy-dd-MM hh:mm:ss"));
}


void ClientGUI::handelSignalDeviceSearch(const QString& data)
{
    try
    {
        DeviceInfo device_info;
        nlohmann::json body = nlohmann::json::parse(data.toStdString());
        device_info.name = body.value("name", "");
        device_info.serial = body.value("serial", "");
        device_info.remark = body.value("remark", "");
        device_info.ip = body.value("ip", "");
        device_info.subnet = body.value("subnet", "");
        device_info.ntp_url = body.value("ntp_url", "");
        map_device_info[device_info.serial] = device_info;
        QStringList device_list{};
        for (const auto& [key, value] : map_device_info)
        {
            device_list << QString::fromStdString(value.serial);
        }
        model_device_list->setStringList(device_list);
    }
    catch (const std::exception& e)
    {
        emit signalLog(QString(u8"数据解析失败。%1").arg(QString::fromStdString(e.what())));
        return;
    }
}
